#include <cassert>
#include <cstring>
#include <iostream>
#include <cstdlib>   

#ifdef WIN32
#include <time.h>
#else
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#endif

#include "udp.h"
#include "stun.h"

int lport=0;
int uport=0;
char uip[200];

using namespace std;

#define MAX_NIC 3

int main(int argc, char* argv[])
{

	
    lport=0;
    uport=0;
	initNetwork();	
	int testNum = 0;	
	bool verbose = true;	
	StunAddress4 stunServerAddr;	
	stunServerAddr.addr=0;	
	int srcPort=0;	
	StunAddress4 sAddr[MAX_NIC];	
	int retval[MAX_NIC];	
	int numNic=0;	
	for ( int i=0; i<MAX_NIC; i++ ) 
		{		
		sAddr[i].addr=0;		
		sAddr[i].port=0;		
		retval[i]=0;
		}
	
	
		if ( srcPort == 0 )
			{	
			srcPort = stunRandomPort(); 
			lport=srcPort;
			}	
		if ( numNic == 0 )	
			{		numNic = 1; }
		
		stunParseServerName( "211.149.233.137", stunServerAddr);

   
   for ( int nic=0; nic<numNic; nic++ )
   {
      sAddr[nic].port=srcPort;
      if ( stunServerAddr.addr == 0 )
      {
       //  usage();
         exit(-1);
      }
   
      if (testNum==0)
      {
         bool presPort=false;
         bool hairpin=true;
		
         NatType stype = stunNatType( stunServerAddr, verbose, &presPort, &hairpin, 
                                      srcPort, &sAddr[nic]);
		
         if ( nic == 0 )
         {
            cout << "Primary: ";
         }
         
         switch (stype)
         {
            case StunTypeFailure:
               cout << "Some stun error detetecting NAT type";
	       retval[nic] = -1;
               exit(-1);
               break;
            case StunTypeUnknown:
               cout << "Some unknown type error detetecting NAT type";
	       retval[nic] = 0xEE;
               break;
            case StunTypeOpen:
               cout << "Open";
	       retval[nic] = 0x00; 
               break;
            case StunTypeIndependentFilter:
               cout << "Independent Mapping, Independent Filter";
               if ( presPort ) cout << ", preserves ports"; else cout << ", random port";
               if ( hairpin  ) cout << ", will hairpin"; else cout << ", no hairpin";
               retval[nic] = 0x02;
               break;
            case StunTypeDependentFilter:
               cout << "Independent Mapping, Address Dependent Filter";
               if ( presPort ) cout << ", preserves ports"; else cout << ", random port";
               if ( hairpin  ) cout << ", will hairpin"; else cout << ", no hairpin";
               retval[nic] = 0x04;
               break;
            case StunTypePortDependedFilter:
               cout << "Independent Mapping, Port Dependent Filter";
               if ( presPort ) cout << ", preserves ports"; else cout << ", random port";
               if ( hairpin  ) cout << ", will hairpin"; else cout << ", no hairpin";
               retval[nic] = 0x06;
               break;
            case StunTypeDependentMapping:
               cout << "Dependent Mapping";
               if ( presPort ) cout << ", preserves ports"; else cout << ", random port";
               if ( hairpin  ) cout << ", will hairpin"; else cout << ", no hairpin";
               retval[nic] = 0x08;
               break;
            case StunTypeFirewall:
               cout << "Firewall";
               retval[nic] = 0x0A;
               break;
            case StunTypeBlocked:
               cout << "Blocked or could not reach STUN server";
               retval[nic] = 0x0C;
               break;
            default:
               cout << stype;
               cout << "Unkown NAT type";
               retval[nic] = 0x0E;  // Unknown NAT type
               break;
         }
         cout << "\t"; cout.flush();
         
         if (!hairpin)
         {
             retval[nic] |= 0x10;
         }       

         if (presPort)
         {
             retval[nic] |= 0x01;
         }
      }
      else if (testNum==100)
      {
         Socket myFd = openPort(srcPort,sAddr[nic].addr,verbose);
      
         StunMessage req;
         memset(&req, 0, sizeof(StunMessage));
      
         StunAtrString username;
         StunAtrString password;
         username.sizeValue = 0;
         password.sizeValue = 0;
      
         stunBuildReqSimple( &req, username, 
                             false , false , 
                             0x0c );
      
         char buf[STUN_MAX_MESSAGE_SIZE];
         int len = STUN_MAX_MESSAGE_SIZE;
      
         len = stunEncodeMessage( req, buf, len, password,verbose );
      
         if ( verbose )
         {
            cout << "About to send msg of len " << len 
                 << " to " << stunServerAddr << endl;
         }
      
         while (1)
         {
            for ( int i=0; i<100; i++ )
            {
               sendMessage( myFd,
                            buf, len, 
                            stunServerAddr.addr, 
                            stunServerAddr.port,verbose );
            }
#ifdef WIN32 // !cj! TODO - should fix this up in windows
            clock_t now = clock();
            assert( CLOCKS_PER_SEC == 1000 );
            while ( clock() <= now+10 ) { };
#else
            usleep(10*1000);
#endif
         }
      }
      else if (testNum==-2)
      {
         const int numPort = 5;
         int fd[numPort];
         StunAddress4 mappedAddr;
         
         for( int i=0; i<numPort; i++ )
         {
            fd[i] = stunOpenSocket( stunServerAddr, &mappedAddr,
                                    (srcPort==0)?0:(srcPort+i), &sAddr[nic],
                                    verbose );
            cout << "Got port at " << mappedAddr.port << endl;
         }
          
         for( int i=0; i<numPort; i++ )
         {
            closesocket(fd[i]);
         }
      }
      else if (testNum==-1)
      {
         int fd3,fd4;
         StunAddress4 mappedAddr;
         
         bool ok = stunOpenSocketPair(stunServerAddr,
                                      &mappedAddr,
                                      &fd3,
                                      &fd4,
                                      srcPort, 
                                      &sAddr[nic],
                                      verbose);
         if ( ok )
         {
            closesocket(fd3);
            closesocket(fd4);
            cout << "Got port pair at " << mappedAddr.port << endl;
         }
         else
         {
            cerr << "Opened a stun socket pair FAILED" << endl;
         }
      }
      else
      {
         stunTest( stunServerAddr,testNum,verbose,&(sAddr[nic]) );
      }
   } // end of for loop 
   cout << endl;
   
   UInt32 ret=0;
   for ( int i=numNic-1; i>=0; i-- )
   {
      if ( retval[i] == -1 )
      {
         ret = 0xFFFFFFFF;
         break;
      }
      ret = ret << 8;
      ret = ret | ( retval[i] & 0xFF );
   }
   
   cout << "Return value is " << hex << "0x";
   cout.fill('0');
   cout.width(6);
   cout << ret << dec << endl;
   cout.fill(' ');
   
   return ret;
}



